/* * Copyright (C) 2013 jonas.oreland@gmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.runnerup.util; import android.annotation.TargetApi; import android.os.Build; import android.util.Base64; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.util.Random; import javax.crypto.Cipher; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; import javax.crypto.spec.SecretKeySpec; @TargetApi(Build.VERSION_CODES.FROYO) public class Encryption { private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; /** * Computes RFC 2104-compliant HMAC signature. * * @param data The data to be signed. * @param key The signing key. * @return The Base64-encoded RFC 2104-compliant HMAC signature. * @throws java.security.SignatureException when signature generation fails */ public static String calculateRFC2104HMAC(final String data, final String key) throws java.security.SignatureException { try { // get an hmac_sha1 key from the raw key bytes final SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM); // get an hmac_sha1 Mac instance and initialize with the signing key final Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); mac.init(signingKey); // compute the hmac on input data bytes final byte[] rawHmac = mac.doFinal(data.getBytes()); // base64-encode the hmac return android.util.Base64.encodeToString(rawHmac, Base64.NO_WRAP); } catch (final Exception e) { throw new SignatureException("Failed to generate HMAC : " + e.getMessage()); } } private static final String CRYPT_ALGORITHM = "PBEWithMD5AndDES"; /** * @param in * @param out * @param key * @throws Exception */ public static void encrypt(final InputStream in, final OutputStream out, final String key) throws Exception { final PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray()); final SecretKeyFactory keyFactory = SecretKeyFactory .getInstance(CRYPT_ALGORITHM); final SecretKey passwordKey = keyFactory.generateSecret(keySpec); // PBE = hashing + symmetric encryption. A 64 bit random // number (the salt) is added to the password and hashed // using a Message Digest Algorithm (MD5 in this example.). // The number of times the password is hashed is determined // by the iteration count. Adding a random number and // hashing multiple times enlarges the key space. final byte[] salt = new byte[8]; final Random rnd = new Random(); rnd.nextBytes(salt); // Create the parameter spec for this salt and iteration count final int iterations = 100; final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, iterations); // Create the cipher and initialize it for encryption. final Cipher cipher = Cipher.getInstance(CRYPT_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, passwordKey, parameterSpec); // Need to write the salt to the (encrypted) file. The // salt is needed when reconstructing the key for decryption. out.write(salt); // Read the file and encrypt its bytes. final byte[] input = new byte[64]; int bytesRead; while ((bytesRead = in.read(input)) != -1) { final byte[] output = cipher.update(input, 0, bytesRead); if (output != null) out.write(output); } final byte[] output = cipher.doFinal(); if (output != null) out.write(output); in.close(); out.flush(); out.close(); } public static void decrypt(final InputStream in, final OutputStream out, final String key) throws Exception { final PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray()); final SecretKeyFactory keyFactory = SecretKeyFactory .getInstance(CRYPT_ALGORITHM); final SecretKey passwordKey = keyFactory.generateSecret(keySpec); // Read in the previously stored salt and set the iteration count. final byte[] salt = new byte[8]; in.read(salt); final int iterations = 100; final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, iterations); // Create the cipher and initialize it for decryption. final Cipher cipher = Cipher.getInstance(CRYPT_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, passwordKey, parameterSpec); final byte[] input = new byte[64]; int bytesRead; while ((bytesRead = in.read(input)) != -1) { final byte[] output = cipher.update(input, 0, bytesRead); if (output != null) out.write(output); } final byte[] output = cipher.doFinal(); if (output != null) out.write(output); in.close(); out.flush(); out.close(); } public static void main(final String args[]) { if (args.length == 2) { final String name = args[0]; final String key = args[1]; try { final FileInputStream in = new FileInputStream(name); final FileOutputStream out = new FileOutputStream(name + ".des"); encrypt(in, out, key); } catch (final Exception e) { e.printStackTrace(); } } } public static byte[] SHA1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException { final MessageDigest digest = MessageDigest.getInstance("SHA-1"); return digest.digest(text.getBytes("UTF-8")); } public static byte[] md5(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException { final MessageDigest digest = MessageDigest.getInstance("MD5"); return digest.digest(text.getBytes("UTF-8")); } public static String toHex(byte bytes[]) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02X", b)); } return sb.toString(); } }